package com.gxa.modules.service.impl;

import com.gxa.common.constant.Constants;
import com.gxa.common.exception.LcException;
import com.gxa.common.utils.JwtUtils;
import com.gxa.common.vo.PageRecv;
import com.gxa.common.vo.ResponseToken;
import com.gxa.common.vo.ResultVo;
import com.gxa.modules.mapper.DeptMapper;
import com.gxa.modules.mapper.RoleMapper;
import com.gxa.modules.mapper.UserMapper;
import com.gxa.modules.pojo.Dept;
import com.gxa.modules.pojo.Menu;
import com.gxa.modules.pojo.Role;
import com.gxa.modules.pojo.User;
import com.gxa.modules.service.UserService;
import com.wf.captcha.SpecCaptcha;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;


@Service
public class UserServiceImpl implements UserService {

    @Autowired(required = false)
    UserMapper userMapper;

    @Autowired(required = false)
    RedisTemplate redisTemplate;

    @Autowired(required = false)
    RoleMapper roleMapper;

    @Autowired(required = false)
    DeptMapper deptMapper;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResultVo addUser(User user) {
        ResultVo r=new ResultVo();
        //查询部门是否存在
        if(user.getDept()!=null && user.getDept().getDept_name()!=null){
            Dept dept=deptMapper.queryDeptByName(user.getDept().getDept_name());
            if(dept==null){
                throw new LcException(400,"添加的部门不存在");
            }
            user.setDept_id(dept.getDept_id());
        }

        //密码加密
        String password=user.getUser_password();
        String salt="salt";//密码盐
        int hashIterations=2;//hash次数
        SimpleHash simpleHash=new SimpleHash("MD5",password,salt,hashIterations);
        String newPassword=simpleHash.toString();
        user.setUser_password(newPassword);
        user.setUser_salt(salt);
        int result=userMapper.insertUser(user);
        if(result<=0){
            throw new LcException(500,"添加成员失败，请联系管理员！");
        }

        System.out.println(user.getRole().getR_title());
        //查询角色是否存在
        if (user.getRole()!=null && user.getRole().getR_title() != null) {
            Role role=roleMapper.queryRoleByTitle(user.getRole().getR_title());
            if(role==null){
                throw new LcException(400,"添加的角色不存在");
            }
            int insertResult=userMapper.insertRoleUser(user.getUser_id(),role.getR_id());
            if(insertResult<=0){
                throw new LcException(500,"添加成员角色信息失败");
            }
        }

        r.setCode(200);
        r.setMsg("添加成员成功！");
        return r;
    }

    /**
     * 1.shiro登录认证
     * 2.shiro认证通过，生成token
     * 3.将生成的token存入redis，并设置生存周期
     */

    /**
     * 其他接口:
     *   每次访问都必须先去redis查询token是否存活且token是否一致（不一致说明有人登录，导致token不一致，此时应该提示有人将其挤下线了）：（使用filter？）
     * 存活的情况：
     * 0.验证shiro权限
     *1.正常访问接口
     * 2.接口访问完成后需要重新设置redis的存活时间
     *
     * 失效的情况：
     * 说明用户在设置的时间内未访问过，故需要失效来自动退出登录状态
     */

    /**
     * 登录挤下线的功能：
     * 由于每次登录时都会把token存入redis，若redis中的token和客户端传递来的值不一致，说明有人登录，导致token不一致，此时应该提示有人将其挤下线了
     */
    @Override
    public ResponseToken login(User user, String captchaKey, String captchaValue, HttpServletRequest request) {
        System.out.println("前端验证码"+captchaValue);
        ResponseToken r=new ResponseToken();
        //从redis获取验证码
        String captchaValueRedis=(String)redisTemplate.opsForValue().get(captchaKey);
        System.out.println("redis上的验证码"+captchaValueRedis);
        if(captchaValueRedis==null){
            throw new LcException(400,"验证码已过期");
        }
        if(!captchaValueRedis.equalsIgnoreCase(captchaValue)){
            throw new LcException(400,"验证码错误");
        }

        System.out.println("验证码正确");

        //免登录
//        String headerToken=request.getHeader("token");
//        if(headerToken!=null){
//            JwtResult result = JwtUtils.validateJwt(headerToken);
//            //通过uuid在redis中获取token
//            String subjStr=result.getClaims().getSubject();
//            String oldToken = (String)redisTemplate.opsForValue().get(subjStr);
//
//            //若前端传递的token和redis上的一样，且token本身未过期，可直接登录成功
//            if(oldToken!=null && oldToken.equals(headerToken)){
//                r.setCode(200);
//                r.setMsg("登录成功");
//                return r;
//            }
//        }

        Subject subject= SecurityUtils.getSubject();
        UsernamePasswordToken token=new UsernamePasswordToken(user.getUser_account(),user.getUser_password());
        token.setRememberMe(true);
        subject.login(token);

        //登录成功后生成token
        String subjectStr=JwtUtils.generalSubject(user);
        String newToken = JwtUtils.createJwt(UUID.randomUUID().toString(),
                "gxa_jwt_test",subjectStr,JwtUtils.TTMILLIS*6*60*24);//token本身存活一天

        //subjectStr作为redis中的key
        redisTemplate.opsForValue().set(subjectStr,newToken,30,TimeUnit.MINUTES);//存活半小时
        System.out.println("存入redis中的key:"+subjectStr+"\n存入redis中的token"+newToken);

        User u=userMapper.queryUserByAccount(user.getUser_account());
        u.setUser_salt("");//抹掉密码盐
        u.setUser_password("");//抹掉密码
        r.setCode(200);
        r.setMsg("登录成功");
        r.setData(u);
        r.setToken(newToken);
        return r;
    }


    /**
     *  验证码存入redis（可以自由控制失效时间），键值是一个随机uuid，并将这个uuid返给前端，前端验证时带过来，再去redis中找，再判断是否相等，不使用session
     */
    @Override
    public ResultVo getCaptcha() {
        ResultVo r=new ResultVo();
        SpecCaptcha specCaptcha = new SpecCaptcha(120, 40, 4);
        //获取图片中的值
        String verCode = specCaptcha.text().toLowerCase();
        String captchaKey=UUID.randomUUID().toString();
        redisTemplate.opsForValue().set(captchaKey,verCode,120, TimeUnit.SECONDS);
//        String num=(String)redisTemplate.opsForValue().get(captchaKey);
        Map map = new HashMap<>();
        map.put("captcha",specCaptcha.toBase64());
        map.put("captchaKey",captchaKey);
        System.out.println("captchaKey:"+captchaKey+",verCode:"+verCode);
        r.setCode(200);
        r.setData(map);
        return r;
    }


    @Override
    public ResultVo getUserPageByCondition(PageRecv<User> userPageRecv) {
        ResultVo r=new ResultVo();
        List<User> userList=userMapper.queryUserPageByCondition(userPageRecv);

        r.setCode(200);
        r.setData(userList);
        return r;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResultVo modifyUser(User user) {
        ResultVo r=new ResultVo();
        //查询部门是否存在
        if(user.getDept()!=null && user.getDept().getDept_name()!=null){
            Dept dept=deptMapper.queryDeptByName(user.getDept().getDept_name());
            if(dept==null){
                throw new LcException(400,"部门不存在");
            }
            user.setDept_id(dept.getDept_id());
        }
        //查询角色是否存在
        if (user.getRole()!=null && user.getRole().getR_title() != null) {
            Role role=roleMapper.queryRoleByTitle(user.getRole().getR_title());
            if(role==null){
                throw new LcException(400,"添加的角色不存在");
            }

            //删除改用户原有的所有角色
            int delResult=userMapper.delRoleUser(user.getUser_id());
            if(delResult<=0){
                throw new LcException(500,"删除原有角色失败");
            }
            //添加角色
            int insertResult=userMapper.insertRoleUser(user.getUser_id(),role.getR_id());
            if(insertResult<=0){
                throw new LcException(500,"添加角色失败");
            }
        }
        int updateResult=userMapper.updateUser(user);
        if(updateResult<=0){
            throw new LcException(500,"修改用户失败");
        }
        r.setCode(200);
        r.setMsg("修改用户成功");
        return r;
    }

    @Override
    public ResultVo delUser(User user) {
        ResultVo r=new ResultVo();
        user.setUser_enable(Constants.USER_DEL_STATUS);
        int delResult=userMapper.updateUser(user);
        if(delResult<=0){
            throw new LcException(500,"删除用户失败");
        }
        r.setCode(200);
        r.setMsg("删除用户成功");
        return r;
    }

    @Override
    public ResultVo getPermission(User user) {
        ResultVo r=new ResultVo();
        List<Menu> menus=userMapper.selectMenuByUserId(user.getUser_id());
        r.setCode(200);
        r.setData(menus);
        return r;
    }

    @Override
    public ResultVo logout(User user) {
        ResultVo r=new ResultVo();
        r.setCode(401);
        r.setMsg("你已退出登录");
        //清除redis上的token(可以不清)
        //清除shiro中的session
        Subject subject= SecurityUtils.getSubject();
        subject.logout();
        return r;
    }
}
